EP17. 컨텍스트 증강과 RAG
LLM 입력 컨텍스트 예산을 관리하는 컨텍스트 엔지니어링의 관점에서 RAG의 표준 정의(임베딩 모델 + 코사인 유사도)와 검색 품질을 좌우하는 청킹 전략을 학습합니다.
0. 사전 필수 용어 (선행지식)
본 EP 를 이해하려면 다음 5개 용어를 먼저 숙지하세요. 깊은 선수 지식은 AI 엔지니어링 EP0 - 선수지식 또는 Hugging Face NLP Course 를 참고합니다.
-
컨텍스트 윈도우 (Context Window) LLM이 한 번에 입력으로 받을 수 있는 토큰 수 상한. "도서관에 있는 책상 한 칸에 한 번에 올릴 수 있는 책 더미 두께"에 비유할 수 있습니다. 본문 §1·§2 의 "입력 컨텍스트 예산" 용어와 직결됩니다.
-
임베딩 (Embedding) / 밀집 벡터 (Dense Vector) 문자열·이미지·사운드 등 입력을 의미를 보존하는 고정 길이 실수 벡터(예: 1536차원)로 변환한 결과. "단어를 의미 좌표계의 점으로 찍는 것"으로 비유합니다. KNN·로지스틱 회귀 등으로 만든 통계 벡터와 구분되며, 임베딩 모델은 신경망 학습 산물입니다. 본문 §3·§4 의 RAG 정의 조건에 반복 등장.
-
코사인 유사도 (Cosine Similarity) 두 벡터의 방향이 얼마나 같은가를 [-1, 1] 로 측정. "두 화살표가 같은 방향을 가리키면 1, 직각이면 0, 정반대면 -1". 임베딩 모델의 손실 함수가 이 척도로 설계돼 있어 RAG 의 표준 검색 거리 함수가 됩니다. 본문 §3 RAG 표준 조건·§6 핵심 원리에서 사용.
-
벡터 데이터베이스 (Vector DB) 임베딩 벡터를 저장하고 코사인 유사도 기반 ANN(근사 최근접 이웃) 검색을 빠르게 수행하는 저장소. Chroma·Qdrant·pgvector·Milvus·Weaviate 등이 대표적입니다. 본문 §4 의 "멀티벡터 스토어"·§5 의 OSS 인용에서 사용.
-
리트리버 (Retriever) 사용자 질의를 받아 임베딩화·검색·점수 필터링·재랭킹까지 한 인터페이스 뒤에 묶은 구성 요소. LangChain/LlamaIndex 에서 모듈 단위로 제공됩니다. 본문 §3·§7·§10 의 패턴 인용에서 사용.
1. 주제 정의
컨텍스트 증강(Context Augmentation) 은 LLM이 한 번에 받을 수 있는 입력 토큰 한도(컨텍스트 예산) 안에 사용자 질의·프롬프트·배경 지식·대화 기억·도구 사용 정보 등 응답 품질에 기여하는 모든 요소를 선별적으로 채워 넣는 설계 활동입니다. 이 활동을 체계화한 영역을 컨텍스트 엔지니어링(Context Engineering) 이라 부르며, RAG는 그 중 "배경 지식" 슬롯을 외부 검색으로 채우는 대표 기법입니다.
핵심 아이디어: 프롬프트 한 줄을 정교화하는 차원을 넘어, 제한된 입력 예산을 어디에 얼마나 배분할지 최적화하는 것이 RAG·도구 사용·메모리·페르소나를 통합 관리하는 출발점입니다.
2. 풀려는 문제
LLM을 실서비스에 투입하면 다음 4가지 컨텍스트 자원 충돌이 반복적으로 나타납니다.
- 문제 1 — 컨텍스트 예산 초과: MCP 서버 200개·긴 대화 기억·도구 설명을 모두 넣으면 정작 배경 지식 토큰이 부족해 응답 품질이 무너집니다.
- 문제 2 — 환각(Hallucination): 모델 학습 시점 이후 정보, 사내 도메인 데이터, 실시간 가격·날씨 등을 알지 못해 그럴듯한 거짓을 답변합니다.
- 문제 3 — 검색 품질 저하: RAG를 도입해도 청킹이 거칠면 "잘린 문장 조각"이 임베딩 되어 의미론적 검색이 사용자 의도를 못 잡습니다.
- 문제 4 — 멀티미디어 입력 한도: 한 장의 큰 이미지·긴 사운드·동영상도 모델별 인풋 사이즈 제한이 있어 그대로 넣을 수 없습니다.
💡 실무 노하우: 컨텍스트 예산은 단순히 "토큰 수"가 아니라 비용·지연시간·정확도의 3축 트레이드오프입니다. 입력이 길어지면 (a) 비용 선형 증가, (b) Time-to-First-Token 증가, (c) Lost-in-the-Middle 현상으로 중간 위치 정보 회상률이 떨어집니다. 예산을 무작정 키우기보다 우선순위 큐로 슬롯을 관리하세요.
3. 핵심 개념·구조
컨텍스트 증강 시스템은 다음 요소로 구성됩니다.
- 입력 컨텍스트 슬롯: 시스템 프롬프트, 유저 쿼리, 대화 기록, 사용자 프로필, 도구 정의, RAG 검색 결과, 실시간 정보.
- 컨텍스트 예산(Context Budget): 모델 카드의 max input tokens 에서 출력 예약분을 뺀 잔여 토큰량.
- RAG 컴포넌트: Document Loader → Chunker → Embedder → Vector Store → Retriever → (선택) Re-ranker → LLM.
- 선택 옵션: 리랭커, 메타 필터 검색, 본문 키워드(BM25/LIKE) 결합, 사용자 질의 재작성(Query Rewriting), 멀티벡터 스토어.
┌────────────────────────────────────────────────┐
│ Context Budget (예: 100K tokens) │
├────────────────────────────────────────────────┤
│ [system] [user query] [history] [profile] │
│ [tool defs] [RAG hits] [realtime] [response ↓] │
└────────────────────────────────────────────────┘
│
▼ RAG 슬롯 채우기
┌──────────────────────────────────────┐
│ user query │
│ │ (선택) query rewrite (LLM) │
│ ▼ │
│ embed → vector DB → top-k chunks │
│ │ │
│ ▼ (선택) re-rank (LLM/cross-enc) │
│ context payload ─────► prompt │
└──────────────────────────────────────┘
업계는 (a) 임베딩 모델로 밀집 벡터 생성 + (b) 코사인 유사도 기반 검색 — 이 두 조건을 만족하는 시스템만을 RAG(=시맨틱 서치)로 인정합니다.
4. 구현 가이드 (Do It Yourself)
시작 전 (Before you begin)
이 섹션을 완료하면 PDF 한 권을 청킹·임베딩·검색하여 LLM 답변에 주입하는 최소 RAG 파이프라인을 직접 만들 수 있습니다.
선수 조건:
- Python 3.10 이상
- pip install langchain langchain-community langchain-openai langchain-anthropic chromadb pypdf sentence-transformers
- 환경 변수: ANTHROPIC_API_KEY (또는 OPENAI_API_KEY)
소요 시간: 약 25분.
Step 1 — 문서 로딩과 청킹 전략 정하기
목표: PDF 한 권을 임베딩 모델 가이드라인에 맞춘 길이로 분할합니다.
다음 코드를 rag_pipeline.py 에 추가합니다.
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
loader = PyPDFLoader("docs/ai_engineering_book.pdf")
documents = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", ". ", " ", ""],
)
chunks = splitter.split_documents(documents)
print(f"총 {len(chunks)}개 청크 생성")
이 코드는 PDF를 페이지 단위 Document 로 읽고, RecursiveCharacterTextSplitter 가 단락→문장→공백 순으로 의미 경계를 우선해 분할합니다. chunk_size=1000·chunk_overlap=200 은 출발점이며, 실제 값은 사용할 임베딩 모델 카드의 권장치를 따르세요.
⚠️ 주의: 청크 길이는 임의로 정하지 마세요. 임베딩 모델마다 "어떤 길이에서 검색 일치도가 가장 높은지" 권장 값이 다릅니다. OpenAI
text-embedding-3-small·Cohereembed-multilingual-v3·BAAI/bge-m3모두 권장치가 다릅니다.💡 실무 노하우: 청크가 너무 길면 컨텍스트 예산을 크게 잠식하고 핵심 정보 추출 난도가 오릅니다. 너무 짧으면 한 청크가 독립적 의미를 잃어 검색은 되어도 답변 생성에 실패합니다. 한국어 기술 문서 기준 600~1200자 + 10~20% overlap 으로 시작해 검색 품질 지표(
nDCG@k)로 조정하세요.📚 참고: LangChain의 Text Splitters 문서와 LlamaIndex의 NodeParser 가이드.
✅ 확인: print 출력으로 청크 수가 0보다 크고, 임의의 chunks[0].page_content 가 완결된 문장 흐름을 포함하면 성공입니다.
Step 2 — 임베딩 + 벡터 DB 색인
목표: 청크를 밀집 벡터로 변환해 코사인 유사도 검색이 가능한 벡터 DB 에 저장합니다.
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_chroma import Chroma
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-m3",
model_kwargs={"device": "cuda"},
encode_kwargs={"normalize_embeddings": True},
)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
collection_metadata={"hnsw:space": "cosine"},
persist_directory="./chroma_db",
)
normalize_embeddings=True 와 hnsw:space="cosine" 조합으로 코사인 유사도 검색이 활성화됩니다. BAAI/bge-m3 는 한국어·영어를 한 모델로 처리하며 100+ 언어를 지원합니다.
⚠️ 주의: 코사인 유사도를 쓰는 이유는 임베딩 모델의 손실 함수가 코사인 유사도로 학습되었기 때문입니다. 유클리드 거리·내적은 동작은 하지만 모델 학습 분포와 어긋나 품질이 떨어질 수 있습니다.
📚 참고: HuggingFace Sentence Transformers, Chroma 공식 문서.
✅ 확인: ./chroma_db/ 디렉토리가 생성되고 .sqlite3 파일이 100KB 이상이면 색인이 영속화된 것입니다.
Step 3 — 리트리버 + LLM 응답 생성
목표: 사용자 질의를 받아 top-k 청크를 검색하고 LLM 응답에 주입합니다.
from langchain_anthropic import ChatAnthropic
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
prompt = ChatPromptTemplate.from_template(
"다음 컨텍스트만 근거로 답하세요. 모르면 모른다고 답하세요.\n"
"<context>\n{context}\n</context>\n\n질문: {question}"
)
llm = ChatAnthropic(model="claude-sonnet-4-6", temperature=0.2, max_tokens=1024)
def format_docs(docs):
return "\n\n".join(d.page_content for d in docs)
chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
print(chain.invoke("청킹 길이는 어떻게 결정하나요?"))
k=4 는 출발 값입니다. 본문 검색(BM25) 결합·메타 필터·리랭킹 도입 전 단순 의미론적 검색만으로 베이스라인을 측정하세요.
💡 실무 노하우: LangChain 의 LCEL(LangChain Expression Language)
|연산자로 컴포넌트를 합성하면 스트리밍·배치·async 가 모두 자동 지원됩니다. 같은 체인을chain.stream(...)·chain.abatch([...])로 즉시 사용할 수 있습니다.📚 참고: Anthropic Messages API, LangChain LCEL 가이드.
✅ 확인: 응답에 PDF 본문에서 등장한 고유 용어가 인용되어 있다면 RAG 가 정상 작동한 것입니다.
Step 4 — 동작 확인 (테스트)
queries = [
"컨텍스트 엔지니어링이란 무엇인가?",
"RAG 가 업계에서 인정받기 위한 두 조건은?",
"청킹 길이를 결정하는 가장 중요한 기준은?",
]
for q in queries:
print(f"\nQ: {q}")
print(f"A: {chain.invoke(q)[:200]}...")
예상 출력 (요지):
Q: RAG 가 업계에서 인정받기 위한 두 조건은?
A: (1) 임베딩 모델로 밀집 벡터를 생성할 것, (2) 검색 시 코사인 유사도 기반 알고리즘을 사용할 것 ...
세 질의 모두 PDF 본문 근거 인용이 보이면 최소 파이프라인이 완성된 것입니다.
5. 적용 사례 (공신력 오픈소스)
- LangChain (
https://github.com/langchain-ai/langchain) — Chain·Retriever·Splitter 표준 인터페이스 제공. - LlamaIndex (
https://github.com/run-llama/llama_index) — RAG·인덱싱·NodeParser 전문 프레임워크. - Anthropic Python SDK (
https://github.com/anthropics/anthropic-sdk-python) — Claude Messages API 공식 SDK. - OpenAI Python SDK (
https://github.com/openai/openai-python) — Embeddings·Chat Completions 공식 SDK. - HuggingFace Transformers (
https://github.com/huggingface/transformers) — 임베딩 모델 호스팅·로컬 추론. - vLLM (
https://github.com/vllm-project/vllm) — PagedAttention 기반 고성능 LLM 서빙(RAG 백엔드). - Ollama (
https://github.com/ollama/ollama) — 로컬 LLM·임베딩 모델 단일 바이너리 실행. - LiteLLM (
https://github.com/BerriAI/litellm) — 100+ 프로바이더 통합 게이트웨이. - Spring AI (
https://github.com/spring-projects/spring-ai) — JVM 진영의 RAG·Embedding 추상화. - LangChain4j (
https://github.com/langchain4j/langchain4j) — Java/Kotlin LangChain 포트.
📚 참고: 위 OSS 는 모두 GitHub stars 5K 이상 또는 Apache·Anthropic·OpenAI 공식 SDK 입니다. 한국어 RAG 패턴 사례는
teddylee777/langchain-kr등 한국어 튜토리얼 레포를 함께 참고하세요.💡 실무 노하우: LangChain 의 LCEL 같은 메타 패턴은 컴포넌트 간 결합도를 낮춰 청킹·임베딩·리트리버·리랭커를 부품처럼 교체하게 해 줍니다. RAG 실험기에서는 LCEL 또는 LlamaIndex
QueryEngine같은 합성 API 로 베이스라인을 빠르게 만든 뒤 측정·최적화에 시간을 더 쓰세요.
6. 핵심 원리
- 컨텍스트는 예산이다. 모든 정보 슬롯이 같은 토큰 풀을 두고 경쟁합니다. RAG·도구·기억은 서로 빼앗는 관계이며, 우선순위 큐와 토큰 회계가 컨텍스트 엔지니어링의 본질입니다.
- RAG = 임베딩 + 코사인 유사도. 업계 표준 정의는 이 두 조건만으로 성립합니다. 손실 함수와 검색 거리 함수가 같을 때만 "의미론적 검색" 이라는 이름이 의미를 갖습니다.
7. 변형·확장
- 하이브리드 검색(Hybrid Retrieval): 밀집 벡터(의미론적) + 희소 벡터/BM25(키워드) 결과를 RRF(Reciprocal Rank Fusion) 으로 합쳐 정확도와 사용자 체감 적합도를 함께 끌어올립니다.
- 메타 필터 검색:
where={"category": "AI"}같은 JSON 필드 조건으로 검색 공간을 좁힌 뒤 벡터 유사도를 적용합니다. - 질의 재작성(Query Rewriting / HyDE): 모호한 사용자 질의를 LLM이 가설 응답으로 변환하고 그 벡터로 검색하면 회상률이 오릅니다.
- 멀티벡터 스토어: 동일 청크에 다양한 임베딩(요약·키워드·원문)을 함께 저장하거나, 동일 임베딩에 다양한 본문 변형을 연결하는 두 방향 모두 지원합니다.
- 에이전트형 RAG(Agentic RAG): 검색을 한 번이 아닌 다단계 도구 호출로 반복하며 답이 충분해질 때까지 자율 결정합니다.
8. 다른 도구·접근과의 비교 (3-way)
- vs Fine-tuning: Fine-tuning 은 모델 파라미터에 지식을 굽지만 변경·롤백 비용이 큽니다. RAG는 외부 색인만 갱신하므로 일·시간 단위 데이터 변화를 즉시 반영합니다.
- vs Long-Context 모델(1M+ 토큰): 긴 컨텍스트 모델은 "넣기만 하면 알아서 찾는다" 는 환상이 있지만 Lost-in-the-Middle·비용·지연 비효율이 큽니다. RAG는 top-k 추출로 비용을 1~2 자릿수 줄입니다.
- vs Knowledge Graph/그래프 RAG: 사실 관계·다단 추론에 강합니다. 일반 자연어 문답·롱테일 도메인 지식엔 의미론적 RAG 가 구현 비용 대비 효과적이며, 그래프 RAG 는 LlamaIndex/Microsoft GraphRAG 처럼 별도 구축 단계가 필요합니다.
9. 한계·트레이드오프
- 검색 품질이 청킹에 종속: 청킹이 거칠면 어떤 임베딩·리트리버를 써도 무너집니다. RAG 시스템 디버깅 시간의 절반 이상이 청킹에 들어가는 일이 흔합니다.
- 임베딩 모델 락인(Lock-in): 한 모델로 색인한 벡터는 다른 모델 벡터와 의미가 다르므로 모델 교체 시 전체 재색인 비용이 발생합니다.
- 신선도(Freshness)와 인덱스 운영: 원천 문서가 빠르게 바뀌면 색인 갱신 파이프라인(증분·삭제·중복 제거)을 별도 운영해야 합니다.
- 보안·PII: 사내 문서가 벡터화되어 외부 임베딩 API 로 흘러가면 정보 유출 위험. 로컬 임베딩(BGE/Sentence-Transformers + Ollama)·온프레미스 벡터 DB 검토가 필요합니다.
10. 최신 권장 패턴 (2025 이후)
- 하이브리드 + RRF 결합: 의미론적 + BM25/키워드 결합이 단일 RAG 보다 사용자 체감 적합도가 높습니다.
- Cross-Encoder Re-ranker:
BAAI/bge-reranker-v2-m3·Cohere Rerank 등으로 top-50 → top-5 재정렬해 정밀도를 끌어올립니다. - 에이전트형 RAG + Tool Use: LangGraph·LlamaIndex AgentWorkflow 로 "검색-판단-재검색" 루프를 명시적으로 설계합니다.
- 구조화 출력(Structured Output): Anthropic Tool Use·OpenAI Structured Outputs 로 RAG 답변을 JSON 스키마로 강제해 후처리 안정성을 확보합니다.
- MCP(Model Context Protocol) 통한 외부 RAG 연결: 같은 RAG 인덱스를 여러 LLM·에이전트가 표준 프로토콜로 공유합니다 (2026-05 기준, Anthropic MCP 공식 문서).
- Multimodal RAG: 텍스트 외 이미지(CLIP·SigLIP)·오디오(스펙트럼 임베딩)도 동일 벡터 공간 또는 별도 컬렉션으로 색인하고 멀티모달 LLM 으로 합성합니다.
11. 메타인지 자기평가
본인 코드/시스템에 RAG 컨텍스트 증강을 적용할 수 있는지 다음 절차로 검증합니다.
Step 1 — 현재 상태 점검
# 사내 문서 후보 디렉토리 크기·파일 수 확인
du -sh ./docs ./wiki ./manuals 2>/dev/null
find ./docs -name "*.pdf" -o -name "*.md" | wc -l
# 현재 프롬프트가 모델 컨텍스트 윈도우 중 얼마를 쓰는지 측정
python -c "import tiktoken; enc=tiktoken.encoding_for_model('gpt-4o'); print(len(enc.encode(open('prompts/system.txt').read())))"
Step 2 — 적용 가능성 평가
- 도메인 지식이 모델 학습 시점 이후이거나 사내·비공개 문서인가?
- 응답마다 인용·근거가 필요한 시나리오(법무·의료·고객 지원)인가?
- 데이터 신선도 요구가 시간·일 단위인가? (실시간 가격은 RAG 보다 Tool Use 가 적합)
- 임베딩·벡터 DB 운영 인프라(스토리지·재색인 파이프라인)를 감당할 수 있는가?
Step 3 — 점진 적용
- 가장 자주 들어오는 FAQ 10개로 정답 셋(평가 셋)을 먼저 만든다.
- 텍스트 PDF/마크다운만으로 베이스라인 RAG(§4 코드)를 구축하고
Recall@k·nDCG@k측정. - 청크 사이즈·overlap·임베딩 모델을 한 번에 한 변수씩 변경하며 측정 비교.
- 그 다음에야 리랭커·하이브리드·메타 필터를 추가하고 다시 측정.
- 마지막으로 멀티모달·에이전트형 RAG 로 확장.
(검증일 2026-05-27, 본 EP 내용 + LangChain·LlamaIndex·Anthropic 공식 문서 기준)
클릭하거나 Space를 눌러 뒤집기